package main.listeners;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class ListenerContainer implements IListenerContainer {
	
	protected List<Object> listeners, addListeners, removeListeners;
	protected boolean iterating;
	protected Map<String, Method> cachecMethods;
	protected Class<?> listenerClass;
	
	public ListenerContainer(Class<?> listenerClass) {
		this.listenerClass = listenerClass;
		
		listeners = new ArrayList<Object>();
		addListeners = new ArrayList<Object>();
		removeListeners = new ArrayList<Object>();
		cachecMethods = new HashMap<String, Method>();
	}
	
	public void callMethod(String methodName) {
		callMethod(methodName, null);
	}
	
	public void callMethod(String methodName, Object arg) {
		
		if(!listeners.isEmpty()) {
			iterating = true;
			
			Method m = getMethod(methodName, arg);
			assert(m != null);
			for(Object l : listeners) {
				try {
					if(arg != null) m.invoke(l, arg);
					else m.invoke(l);
				} catch(Exception e) {
					e.printStackTrace();
				}
			}
			
			iterating = false;
		}
		
		for(Object l : addListeners) {
			listeners.add(l);
		}
		addListeners.clear();
		
		for(Object l : removeListeners) {
			listeners.remove(l);
		}
		removeListeners.clear();
	}
	
	private Method getMethod(String methodName, Object arg) {
		Method m = cachecMethods.get(methodName);
		if(m != null) return m;
		try {
			if(arg != null) m = listenerClass.getMethod(methodName, arg.getClass());
			else m = listenerClass.getMethod(methodName);
			if(m != null) cachecMethods.put(methodName, m);
		} catch(Exception e) {
			e.printStackTrace();
		}
		return m;
	}
	
	public boolean containsListener(Object l) {
		return listeners.contains(l);
	}
	
	@Override
	public void addListener(Object l) {
		assert(l != null);
		
		if(iterating) addListeners.add(l);
		else listeners.add(l);
	}
	
	@Override
	public boolean removeListener(Object l) {
		assert(l != null);
		
		if(iterating) return removeListeners.add(l);
		return listeners.remove(l);
	}
	
	public void cleanUp() {
		listeners.clear();
		addListeners.clear();
		removeListeners.clear();
		cachecMethods.clear();
	}
	
	public int size() { return listeners.size(); }
	
	public Object get(int i) { return listeners.get(i); }
}